<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="oEpSy" id="oEpSy"><span data-lake-id="u02316243" id="u02316243">典型回答</span></h1>
  <p data-lake-id="u8eb1f51d" id="u8eb1f51d"><br></p>
  <p data-lake-id="u576606bb" id="u576606bb"><span data-lake-id="uca436b55" id="uca436b55">代理模式是一种结构设计模式，它允许通过创建代理对象来控制对其他对象的访问。代理对象充当原始对象的接口，客户端通过代理对象间接地访问原始对象，并可以在访问过程中添加额外的逻辑或控制。</span></p>
  <p data-lake-id="ufeb2d838" id="ufeb2d838"><span data-lake-id="u087a72fd" id="u087a72fd">​</span><br></p>
  <p data-lake-id="u77674c8e" id="u77674c8e"><span data-lake-id="u7ea24010" id="u7ea24010">代理模式的主要目的是通过引入代理对象，为原始对象提供一层间接访问的方式，以实现对原始对象的控制、保护或增强。他的常用场景有以下几个：</span></p>
  <p data-lake-id="u31226245" id="u31226245"><span data-lake-id="ub6e327a6" id="ub6e327a6">​</span><br></p>
  <p data-lake-id="u8469e6b3" id="u8469e6b3"><span data-lake-id="ucae26c96" id="ucae26c96">1、远程代理：在分布式系统中，代理模式可用于代理远程对象。远程代理隐藏了远程对象的实际实现细节，使客户端可以像访问本地对象一样访问远程对象。如Dubbo的实现就是用到了代理模式。</span></p>
  <p data-lake-id="uf03e3ccf" id="uf03e3ccf"><span data-lake-id="u8b360b8a" id="u8b360b8a">​</span><br></p>
  <p data-lake-id="u178169df" id="u178169df"><br></p>
  <p data-lake-id="u9dab818c" id="u9dab818c"><span data-lake-id="ud6b21844" id="ud6b21844">2、动态代理：动态代理允许在运行时动态地创建代理对象，并动态地将方法调用分派到不同的处理器。它通过Java的反射机制实现，可以用于实现通用的代理逻辑，而无需为每个被代理的类单独创建代理。如Spring的AOP，就用到了动态代理。</span></p>
  <p data-lake-id="u467bce91" id="u467bce91"><span data-lake-id="u4eef72eb" id="u4eef72eb">​</span><br></p>
  <p data-lake-id="udd3405f0" id="udd3405f0"><br></p>
  <p data-lake-id="u8ccd668d" id="u8ccd668d"><span data-lake-id="ud266990b" id="ud266990b">3、缓存代理：缓存代理可以缓存原始对象的结果，以避免重复计算或访问资源。一般我们在用到缓存的时候，可以用这种模式。先访问代理对象，代理对象会去查询缓存，如果缓存中你没有，再去查询真实对象。</span></p>
  <p data-lake-id="u8c95b33a" id="u8c95b33a"><span data-lake-id="ub1a4fda5" id="ub1a4fda5">​</span><br></p>
  <p data-lake-id="u231d4155" id="u231d4155"><span data-lake-id="u14962e42" id="u14962e42">4、日志代理：这种用的也挺多的，当我需要做日志记录的时候，可以做一个代理，在代理对象中进行统一的日志记录及管理。</span></p>
  <p data-lake-id="u3d3820b2" id="u3d3820b2"><span data-lake-id="uadbd4568" id="uadbd4568">​</span><br></p>
  <p data-lake-id="u89ea8122" id="u89ea8122"><span data-lake-id="u8891366b" id="u8891366b">5、异常代理：通常我们的系统中如果有统一的异常机制或者ERROR_CODE的机制，可以通过创建一个统一的代理来做处理。在代理对象中这些异常的捕捉及转换。</span></p>
  <p data-lake-id="u1544aa4d" id="u1544aa4d"><span data-lake-id="ue7a4baa7" id="ue7a4baa7">​</span><br></p>
  <p data-lake-id="uf22a97a6" id="uf22a97a6"><span data-lake-id="uc8e10e4c" id="uc8e10e4c">​</span><br></p>
  <h1 data-lake-id="c0QGB" id="c0QGB"><span data-lake-id="u9aa51af9" id="u9aa51af9">扩展知识</span></h1>
  <p data-lake-id="u8e05c798" id="u8e05c798"><br></p>
  <h2 data-lake-id="Y3ThE" id="Y3ThE"><span data-lake-id="uee21b7b2" id="uee21b7b2">示例</span></h2>
  <p data-lake-id="u89477576" id="u89477576"><br></p>
  <p data-lake-id="u184a6432" id="u184a6432"><span data-lake-id="ub55e63df" id="ub55e63df">假设我们有一个比较耗时的查询服务，他的接口定义及实现如下：</span></p>
  <p data-lake-id="uf632feb8" id="uf632feb8"><span data-lake-id="u5d63a90c" id="u5d63a90c">​</span><br></p>
  <pre lang="java"><code>
/**
* @Author Hollis
**/
public interface DataService {
    String getData();
}

</code></pre>
  <p data-lake-id="u5e306430" id="u5e306430"><br></p>
  <pre lang="java"><code>
/**
* @Author Hollis
**/
public class DataServiceImpl implements DataService {
    @Override
    public String getData() {
        //执行非常耗时的数据查询
        return "Data from expensive operation";
    }
}
</code></pre>
  <p data-lake-id="ufd3dc7a5" id="ufd3dc7a5"><br></p>
  <p data-lake-id="u0f52c7c6" id="u0f52c7c6"><span data-lake-id="ub89afed5" id="ub89afed5">这时候我们想要引入缓存，则可以定义以一个代理：</span></p>
  <p data-lake-id="u0b4567c8" id="u0b4567c8"><span data-lake-id="ud70c9dd8" id="ud70c9dd8">​</span><br></p>
  <pre lang="java"><code>
public class CachedDataServiceProxy implements DataService {
    private DataService dataService;
    @Autowired
    private CacheService cacheService;

    public CachedDataServiceProxy(DataService dataService) {
        this.dataService = dataService;
    }

    @Override
    public String getData() {
        //先从缓存中获取
        String cachedData = cacheService.getdata();
        //缓存中没有，则查询接口
        if (cachedData == null) {
            
            data = dataService.getData();
            //获取到以后再保存到缓存中
            cacheService.putData(data);
        }
        return cachedData;
    }
}
</code></pre>
  <p data-lake-id="u64c35568" id="u64c35568"><br></p>
  <p data-lake-id="u60fe7956" id="u60fe7956"><span data-lake-id="u7f3ee4c3" id="u7f3ee4c3">写一个测试如下：</span></p>
  <p data-lake-id="uc1666f1e" id="uc1666f1e"><span data-lake-id="ua8d4b575" id="ua8d4b575">​</span><br></p>
  <pre lang="java"><code>
public static void main(String[] args) {
    DataService dataService = new DataServiceImpl();
    DataService cachedDataService = new CachedDataServiceProxy(dataService);
   
    cachedDataService.getData();
}
</code></pre>
  <p data-lake-id="ua7cf2611" id="ua7cf2611"><br></p>
  <p data-lake-id="u41ffabea" id="u41ffabea"><span data-lake-id="u060ae3a2" id="u060ae3a2">以上，就是一个代理模式的具体实现了，一般来说，我们也会基于上面的思想，再结合Spring的AOP的机制，和自定义注解，实现一个简单的注解，可以把他加到方法上就能实现缓存的逻辑了。</span></p>
 </body>
</html>